package com.sec.base.utils;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;

import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

public class BitmapExecute {
	/**
	 * 将BufferedImage转化为bmp文件保存在指定位置
	 *
	 * @param image
	 * @param file
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private static boolean saveBMP(BufferedImage image, File file) {
		// 格式化为bmp文件
		Iterator writers = ImageIO.getImageWritersByFormatName("bmp");
		ImageWriter writer = (ImageWriter) writers.next();
		ImageOutputStream ios = null;
		try {
			FileOutputStream fos = new FileOutputStream(file);
			ios = ImageIO.createImageOutputStream(fos);
			writer.setOutput(ios);
			try {
				writer.write(image);
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			} finally {
				ios.close();
				fos.close();
			}
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}

		return true;
	}

	/**
	 * 将数据文件隐藏入bmp文件中
	 *
	 * @param dataFileName
	 * @param bmpFileName
	 * @param outFileName
	 * @return
	 * @throws IOException
	 */
	public static boolean DataSourceToBMP(String dataFileName, String bmpFileName, String outFileName)
			throws IOException {
		return DataSourceToBMP(new File(dataFileName), new File(bmpFileName), outFileName);
	}

	/**
	 * 将数据文件隐藏入bmp文件中
	 *
	 * @param dataFileName
	 * @param bmpFileName
	 * @param outFileName
	 * @return
	 * @throws IOException
	 */
	@SuppressWarnings("resource")
	public static boolean DataSourceToBMP(File dataFile, File bmpFile, String outFileName) throws IOException {
		FileInputStream dataStream = new FileInputStream(dataFile);
		BufferedImage bmp;
		try {
			bmp = ImageIO.read(bmpFile);
		} catch (Exception ex) {
			return false;
		}
		// 获得要求隐藏文件字节数
		int dataSize = dataStream.available();
		if (dataSize == 0) {
			return false;
		}
		// 本图片允许的最大存储空间字节数
		int maxByteStorage = (bmp.getHeight() * bmp.getWidth() * 3) / 8;
		// 检查图片空间是否够用
		if (dataSize > maxByteStorage) {
			throw new RuntimeException(("Can be hidden space=" + maxByteStorage / 1024 + "KB! However,hidden files="
					+ dataSize / 1024 + "KB!").intern());
		}
		// 读取图片文件
		BitmapOutput bmpWriter = new BitmapOutput(bmp);
		try {
			// 将隐藏数据宽度转为byte[]保存
			byte[] size = IntengerToBytes(dataSize);
			// 在bmp文件中写入隐藏文件大小
			for (int u = 0; u < size.length; u++) {
				bmpWriter.writeByte((byte) size[u]);
			}
			// 输入隐藏数据
			for (int u = 0; u <= dataSize; u++) {
				bmpWriter.writeByte(dataStream.read());
			}
		} catch (Exception ex) {
			ex.getStackTrace();
			return false;
		} finally {
			dataStream.close();
		}
		try {
			File file = new File(outFileName);
			if (file.exists()) {
				file.delete();
			}
			// 保存BufferedImage为bmp文件
			saveBMP(bmpWriter.getBufferedImage(), new File(outFileName));

		} catch (Exception ex) {
			ex.getStackTrace();
			return false;
		}
		return true;
	}

	/**
	 * 从bmp文件中导出隐藏数据(由于隐藏数据的方式不同，只对此类隐藏的有效)
	 *
	 * @param bmpFileName
	 * @param outFName
	 * @return
	 * @throws IOException
	 */
	public static boolean BMPToDataSource(String bmpFileName, String outFName) throws IOException {
		return BMPToDataSource(new File(bmpFileName), outFName);
	}

	/**
	 * 转换整形数据为byte[]
	 *
	 * @param number
	 * @return
	 */
	final private synchronized static byte[] IntengerToBytes(final int number) {
		byte[] bytes = new byte[4];
		for (int i = 0; i < 4; i++) {
			bytes[i] = (byte) ((number >> (i * 8)) & 0xff);
		}
		return bytes;
	}

	/**
	 * 将单独byte转为整形，负值转正
	 *
	 * @param bit
	 * @return
	 */
	final private static int toInteger(final byte bit) {
		if (bit >= 0) {
			return (int) bit;
		} else {
			return (int) (bit + 256);
		}
	}

	/**
	 * 将byte[]转为整形
	 *
	 * @param bytes
	 * @return
	 */
	final private static int bytesForToInt(final byte[] bytes) {
		if (bytes.length != 4) {
			return 0;
		}
		int result = 0;
		try {
			result = toInteger(bytes[0]);
			result = (result << 8) + toInteger(bytes[1]);
			result = (result << 8) + toInteger(bytes[2]);
			result = (result << 8) + toInteger(bytes[3]);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	/**
	 * 从bmp文件中导出隐藏数据(由于隐藏数据的方式不同，只对此类隐藏的有效)
	 *
	 * @param bmpFile
	 * @param outFName
	 * @return
	 * @throws IOException
	 */
	@SuppressWarnings("resource")
	public static boolean BMPToDataSource(File bmpFile, String outFName) throws IOException {
		BufferedImage image = ImageIO.read(bmpFile);
		BitmapInput bmpReader;

		try {
			bmpReader = new BitmapInput(image);
		} catch (Exception ex) {
			return false;
		}
		FileOutputStream outStream;
		try {
			File file = new File(outFName);
			if (!file.exists()) {
				file.createNewFile();
			}
			outStream = new FileOutputStream(file);
		} catch (Exception ex) {
			return false;
		}
		byte[] number = new byte[4];
		int dataSize = 0;
		int outByte = 0;
		try {

			for (int u = number.length - 1; u > 0; u--) {
				// 以对象数组返回body和验证布尔值
				Object[] object = bmpReader.readByte(outByte);
				boolean header = Boolean.parseBoolean((String) object[0]);
				outByte = Integer.parseInt((String) object[1]);
				if (!header) {
					throw new Exception();
				}
				number[u] = (byte) outByte;
			}
			// 将byte[]转回int形式的隐藏文件大小
			dataSize = bytesForToInt(number);
			// 将隐藏文件读出
			for (int u = 0; u <= dataSize; u++) {
				Object[] object = bmpReader.readByte(outByte);
				boolean header = Boolean.parseBoolean((String) object[0]);
				outByte = Integer.parseInt((String) object[1]);
				if (!header) {
					throw new Exception();
				}
				// 第一位不读
				if (u != 0) {
					outStream.write(outByte);
				}
			}
		} catch (Exception ex) {
			ex.printStackTrace();
			return false;
		} finally {
			try {
				outStream.flush();
				outStream.close();
				outStream = null;
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return true;
	}
}
